package game;

import java.util.*;

import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.*;

import java.util.Iterator;
import java.util.Scanner;
import java.util.StringTokenizer;

import game.ItemsProtos.Items;
import java.io.*;

public class Game {

	Vector<Location> locations = new Vector<Location>();
	Location currentLocation;
	Vector<Item> itemList = new Vector<Item>();
	Player player = new Player();

	private static GameGUI gui;
	private GameGUI scanner;
	
	public Game() {
		initialize();
		//Scanner scanner = new Scanner(System.in);
		while(true) {
			gui.setStatus(player.status());
			setGuiImg();
			displayLine("");
			display("Command: ");
			String input = scanner.nextLine();
			displayLine("");
			if (command(input.trim())) {			//Had to trim() the input to make this work with the Regex
				// Executing Commands
				checkWinCondition();				//TEMPORARY check to make sure that Item required tags are working properly.
			} else {
				displayLine("game over");
				break;
			}
		}
	}
	
	public void initialize() {
		gui = new GameGUI();
		scanner = gui;

		String items_xml = "MasterItemList.xml";
		String scenario_xml = "MasterScenario.xml";
		//Scanner scanner = new Scanner(System.in);
		displayLine("Welcome to rootBEER 0.35 SUPER VECTOR PLAY!");
		displayLine("");
		display("Enter the name of the scenario file: ");
		//scenario_xml = scanner.nextLine();
		display("\nEnter your name: ");
		//player.setName(scanner.nextLine());
		
		
		Items.Builder allItems = Items.newBuilder();
		allItems.setName("allItems");
		
		try {
			DOMParser p = new DOMParser();
			p.parse(items_xml);
			Document items_doc = p.getDocument();
			NodeList nodelist = items_doc.getElementsByTagName("item");
			Node node;
			
			for (int i=0; i<nodelist.getLength(); i++) {
				node = nodelist.item(i);
				itemList.add(readItems(node));
				allItems.addItem(buildItem(itemList.lastElement()));
			}
		} catch (Exception e) {e.printStackTrace();}
		
		try {
			DOMParser p = new DOMParser();
			p.parse(scenario_xml);
			Document doc = p.getDocument();
			NodeList nodelist = doc.getElementsByTagName("location");
			Node     node;
			
			for (int i=0; i<nodelist.getLength(); i++) {
				node = nodelist.item(i);
				locations.add(readLocations(node));				// Vector locations list
			}
		} catch (Exception e) {e.printStackTrace();}

		displayLine("");
		currentLocation = locations.elementAt(28);
		displayLine("Good Luck!");
		displayLine("");
		displayLine(currentLocation.getLook());
		displayLine("");

	}
	
	public Item readItems(Node node) {
		Item tmp_item = new Item();
		NodeList children = node.getChildNodes();
		int len = children.getLength();
		for (int i = 0; i<len; i++) {
			node = children.item(i);
			int type = node.getNodeType();
			switch (type) {
			case Node.ELEMENT_NODE:
				if (node.getNodeName().equals("name")) {
					NodeList name = node.getChildNodes();
					tmp_item.setDispName(name.item(0).getNodeValue().replaceAll("[_]", " "));
					tmp_item.setName(standardName(name.item(0).getNodeValue()));
				} else if (node.getNodeName().equals("weight")) {
					NodeList weight = node.getChildNodes();
					tmp_item.setWeight(weight.item(0).getNodeValue());
				} else if (node.getNodeName().equals("required")) {
					NodeList required = node.getChildNodes();
					tmp_item.setIsRequired(required.item(0).getNodeValue());
				} else if (node.getNodeName().equals("canPickup")) {
					NodeList canPickup = node.getChildNodes();
					tmp_item.setCanBeCarried(canPickup.item(0).getNodeValue());
				} else if (node.getNodeName().equals("pickupMessage")) {
					if (node.hasChildNodes()) {	
						NodeList pickupMessage = node.getChildNodes();
						tmp_item.setPickupMessage(pickupMessage.item(0).getNodeValue());
					}
				} else if (node.getNodeName().equals("dropMessage")) {
					if (node.hasChildNodes()) {	
						NodeList dropMessage = node.getChildNodes();
						tmp_item.setDropMessage(dropMessage.item(0).getNodeValue());
					}
				} else if (node.getNodeName().equals("cannotPickupMessage")) {
					if (node.hasChildNodes()) {	
						NodeList cannotPickupMessage = node.getChildNodes();
						tmp_item.setCannotPickupMessage(cannotPickupMessage.item(0).getNodeValue());
					}
				}
			}
		}
		return tmp_item;
	}
	
	public Location readLocations(Node node) {
		Location tmp_location = new Location();
		NodeList children = node.getChildNodes();
		int len = children.getLength();
		for (int i = 0; i<len; i++) {
			node = children.item(i);
			int type = node.getNodeType();
			switch (type) {
			case Node.ELEMENT_NODE:
				if (node.getNodeName().equals("name")) {
					NodeList name = node.getChildNodes();
					tmp_location.setName(name.item(0).getNodeValue());
				} else if (node.getNodeName().equals("description")) {
					NodeList desc = node.getChildNodes();
					tmp_location.setDesc(desc.item(0).getNodeValue());
				} else if (node.getNodeName().equals("image")) {
					NodeList image = node.getChildNodes();
					tmp_location.setImage(image.item(0).getNodeValue());
				} else if (node.getNodeName().equals("doors")) {
					NodeList doors = node.getChildNodes();
					for (int j = 0; j<doors.getLength(); j++) {
						if (doors.item(j).getNodeType() == Node.ELEMENT_NODE) {
							if (doors.item(j).getNodeName().equals("door")) {
								NodeList door = doors.item(j).getChildNodes();
								Door tmp_door = new Door();
								for (int k = 0; k<door.getLength(); k++) {
									if (door.item(k).getNodeName().equals("direction")) {
										NodeList direction = door.item(k).getChildNodes();
										tmp_door.setDirection(direction.item(0).getNodeValue());
									} else if (door.item(k).getNodeName().equals("open")) {
										NodeList open = door.item(k).getChildNodes();
										tmp_door.setOpened(open.item(0).getNodeValue());
									} else if (door.item(k).getNodeName().equals("close")) {
										NodeList closed = door.item(k).getChildNodes();
										tmp_door.setClosed(closed.item(0).getNodeValue());
									} else if (door.item(k).getNodeName().equals("requiredItem")) {
										NodeList reqItem = door.item(k).getChildNodes();
										String tmpItem = standardName(reqItem.item(0).getNodeValue());
										if (isItem(tmpItem)) {
											tmp_door.setReqItem(strToItem(tmpItem));
										} else {
											displayLine("Error Adding setReqItem for Door: " + tmpItem + " doesn't exist!");
										}
									} else if (door.item(k).getNodeName().equals("isOpened")) {
										NodeList isOpened = door.item(k).getChildNodes();
										tmp_door.setIsOpened(Boolean.parseBoolean(isOpened.item(0).getNodeValue()));
									}
								}
								tmp_location.addDoor(tmp_door);
							}
						}
					}
				} else if (node.getNodeName().equals("items")) {
					NodeList items = node.getChildNodes();
					for (int j = 0; j<items.getLength(); j++) {
						if (items.item(j).getNodeType() == Node.ELEMENT_NODE) {
							if (items.item(j).getNodeName().equals("item")) {
								NodeList itemName = items.item(j).getChildNodes();
								for (int k = 0; k<itemName.getLength(); k++) {
									if (itemName.item(k).getNodeName().equals("name")) {
										NodeList name = itemName.item(k).getChildNodes();
										String tmpItem = standardName(name.item(0).getNodeValue());			// create tmp string and remove _ and //
										if (isItem(tmpItem)) {
											tmp_location.addItem(strToItem(tmpItem));
										} else {
											displayLine("Error! Item: " + tmpItem + " doesn't exist!");
										}
									}
								}
							}
						}
					}
				} else if (node.getNodeName().equals("links")) {
					NodeList links = node.getChildNodes();
					for (int j = 0; j<links.getLength(); j++) {
						if (links.item(j).getNodeType() == Node.ELEMENT_NODE) {
							if (links.item(j).getNodeName().equals("link")) {
								NodeList link = links.item(j).getChildNodes();
								String tmpDirection = "";
								String tmpName = "";
								
								for (int k = 0; k<link.getLength(); k++) {
									if (!tmpDirection.equals("") && !tmpName.equals("")) {
										tmp_location.addLink(tmpDirection, tmpName);		//Vector LocationDescriptor
									} 
									if (link.item(k).getNodeName().equals("name")) {
										NodeList name = link.item(k).getChildNodes();
										tmpName = name.item(0).getNodeValue();
										// Add link Name
									} else if (link.item(k).getNodeName().equals("direction")) {
										NodeList direction = link.item(k).getChildNodes();
										tmpDirection = direction.item(0).getNodeValue();
										// Add link direction
									}
								}
							}
						}
					}
				}
				break;
			case Node.TEXT_NODE:

				break;
			}

		}

		//tmp.getLook();
		//displayLine("items " + tmp.itemsHere[0].name);
		return tmp_location;
	}	
	
	public boolean command(String input) {
		Vector<String> input_arr = new Vector<String>();
		String[] tmpArr = input.split(" ");
		for (int i = 0; i<tmpArr.length;i++){
			input_arr.add(tmpArr[i]);
		}
		Iterator<String> iterator = input_arr.iterator();
		while (iterator.hasNext()) {
			String tmp = iterator.next();
			if (tmp.equals("quit")) {
				return false;
			} else if (tmp.equals("take") || tmp.equals("get") || tmp.equals("pickup")) {
				while (iterator.hasNext()) {
					StringBuilder tmp_name = new StringBuilder();
					tmp = iterator.next();
					while (!tmp.contains(",") && !tmp.contains("and") && !tmp.contains("the")) {
						tmp_name.append(tmp);
						if (iterator.hasNext()) {
							tmp = iterator.next();
						} else {
							break;
						}
					}
					if (tmp.contains(",") || tmp.contains("and")) {
						tmp_name.append(tmp.replaceAll(",", ""));
					}
					if (tmp.contains("and")) {
						tmp_name.append(tmp.replaceAll("and", ""));
					}
					if (tmp.contains("the")) {
						tmp_name.append(tmp.replaceAll("the", ""));
					}
					String itemName = tmp_name.toString();
					if (isItem(itemName)) {
						if (strToItem(itemName).canBeCarried) {
							if (currentLocation.removeItem(strToItem(itemName))) {
								player.inventory.addItem(strToItem(itemName));
								currentLocation.removeItem(strToItem(itemName));
								displayLine(strToItem(itemName).pickupMessage);
							} else {
								displayLine(itemName + " is not here.");
							}
						} else {
							displayLine(strToItem(itemName).cannotPickupMessage);
						}
					} else {
						if (itemName.matches("[\\w]")) {				
							displayLine(itemName + " is not an item.");
						}
					}
				}
			} else if (tmp.equals("drop")) {
				while (iterator.hasNext()) {
					StringBuilder tmp_name = new StringBuilder();
					tmp = iterator.next();
					while (!tmp.contains(",") && !tmp.contains("and") && !tmp.contains("the")) {
						tmp_name.append(tmp);
						if (iterator.hasNext()) {
							tmp = iterator.next();
						} else {
							break;
						}
					}
					if (tmp.contains(",")) {
						tmp_name.append(tmp.replaceAll(",", ""));
					}
					if (tmp.contains("and")) {
						tmp_name.append(tmp.replaceAll("and", ""));
					}
					if (tmp.contains("the")) {
						tmp_name.append(tmp.replaceAll("the", ""));
					}
					String itemName = tmp_name.toString();
					if (isItem(itemName)) {
						if (player.inventory.removeItem(strToItem(itemName))) {
							currentLocation.addItem(strToItem(itemName));
							displayLine(strToItem(itemName).dropMessage);
						} else {
							displayLine("Prehaps you already dropped the " + itemName + "?  You cannot drop that which you never even had in the first place.");
						}
					} else {
						if (itemName.matches("[\\w]")) {				
							displayLine(itemName + " is not an item.");
						}
					}
				}
			} else if (tmp.equals("Serialize")) {
				displayLine("Serializing!!!!");
				
				if (!player.inventory.v.isEmpty()) {
					displayLine("Which of the items in your inventory would you like to add to the file? Type done when finished.");
					Items.Builder playersPick = Items.newBuilder();
					Item tmpItem = new Item();
					String choices = "";
					
					while (!choices.equalsIgnoreCase("done")) {
						choices = scanner.nextLine().trim();
						System.out.println("NEW =- " + choices);
						tmpItem = strToItem(choices);
						if (tmpItem != null) {
							playersPick.addItem(buildItem(tmpItem));
						}
					}
					
					display("Enter the name of the file you would like to create: ");
					String fileName = scanner.nextLine().trim();
					
					playersPick.setName(fileName);
					/////////////////////////////// Adding playersPick to Serialized.txt
					
					File file = new File(fileName);
					try {
						file.createNewFile();
					} catch (IOException e) {
						displayLine("ERROR -- Unable to create " + fileName);
					}
					
					try {
						FileOutputStream output = new FileOutputStream(file);
						try {
							playersPick.build().writeTo(output);
							output.close();
						} catch (IOException e) {
							displayLine("IOException");
						}
					} catch (FileNotFoundException e) {
						displayLine("File not found");
					}	
				} else {
					displayLine("Pick up at least one item to use this function.");
					break;
				}
			} else if (tmp.equals("Read")) {
				displayLine("Read Bitch!");
				
				display("Enter the name of the file to read from: ");
				String fileName = scanner.nextLine().trim();
				
				try {
				Items items = Items.parseFrom(new FileInputStream(fileName));
				
				for (Items item: items.getItemList()) {
					displayLine("Item Name: " + item.getName());
					displayLine("Item weight: " + item.getWeight());
					
					for (Items.DisplayMessage displayMessage : item.getMessageList()) {
						switch (displayMessage.getType()) {
						case PICKUP:
							display("  Pickup message: ");
							break;
						case DROP:
							display("  Drop message: ");
							break;
						case CANNOTPICKUP:
							display("Cannot pickup message: ");
							break;
						}
						displayLine(displayMessage.getMessage());
					}
				}
				
				} catch (IOException e) {
					System.out.println(fileName + " cannot be found!");
				}
				
			} else {
				System.out.println("No COMMAND was Performed");
			}
		}
		
		StringTokenizer tokenizer = new StringTokenizer(input);
		String token = "";
		
		while (tokenizer.hasMoreTokens()) {
			token = tokenizer.nextToken();
			if (token.equalsIgnoreCase("quit")) {
				return false;
			//} else if (token.equalsIgnoreCase("north") || token.equalsIgnoreCase("south") || token.equalsIgnoreCase("east") || token.equalsIgnoreCase("west") || token.equalsIgnoreCase("up") || token.equalsIgnoreCase("down") || token.equalsIgnoreCase("northeast") || token.equalsIgnoreCase("northwest") || token.equalsIgnoreCase("southeast") || token.equalsIgnoreCase("southwest") || token.equalsIgnoreCase("enter")){
			} else if (token.equalsIgnoreCase("look")) {
				displayLine(currentLocation.getLook());
				// get look
			} else if (token.equalsIgnoreCase("inventory") || token.equalsIgnoreCase("inv") || token.equalsIgnoreCase("i")) {
				// display player inventory
				displayLine(player.inventory.listInv());
			} else if (token.equalsIgnoreCase("save")) {
				// save the game
				displayLine("Game Saved");
			} else if (token.equalsIgnoreCase("status")) {
				displayLine(player.status());
			} else if (token.equals("listDoors")) {
				listDoors();
			} else if (token.equals("listLinks")) {
				listLinks();
			} else if (token.equals("listLocations")) {
				listLocations();
			} else if (token.equals("listItems")) {
				listItems();
			} else if (!currentLocation.getDirection(token).equals("INVALID_DIRECTION")) {
				if (isLocation(currentLocation.getDirection(token))) {
					if (currentLocation.isDoorOpen(token,player.inventory)) {
						currentLocation = strToLocation(currentLocation.getDirection(token));				//TODO gui.setIcon() HERE!
						displayLine(currentLocation.getLook());
					} else {
						displayLine("You require an item to go this way.");
					}
				} else {
					displayLine("Walking " + token + " could prove difficult as it is at this time impossible. (VALID_DIRECTION + INVALID_LOCATION = YOU_SUCK!");
				}
			} else {
				//displayLine("I cannot do that!");
			}
		}
		return true;
	}
	
	public boolean isItem(String name) {
		boolean isItem = false;
		Iterator<Item> iterator = itemList.iterator();
		while(iterator.hasNext()) {
			Item tmp = iterator.next();
			if (tmp.name.equalsIgnoreCase(name)) {
				isItem = true;
			}
		}
		return isItem;
	}
	
	public Item strToItem(String name) {
		Iterator<Item> iterator = itemList.iterator();
		while(iterator.hasNext()) {
			Item tmp = iterator.next();
			if (tmp.name.equalsIgnoreCase(name)) {
				return tmp;
			}
		}
		return null;
	}
	
	public Location strToLocation(String name) {
		Iterator<Location> iterator = locations.iterator();
		while(iterator.hasNext()) {
			Location tmp_location = iterator.next();
			if (tmp_location.name.equalsIgnoreCase(name)) {
				return tmp_location;
			}
		}
		return null;
	}
	
	public boolean isLocation(String name) {
		boolean isLocation = false;
		Iterator<Location> iterator = locations.iterator();
		while(iterator.hasNext()) {
			Location tmp_location = iterator.next();
			if (tmp_location.name.equalsIgnoreCase(name)) {
				isLocation = true;
			}
		}
		return isLocation;
	}
	
	public void checkWinCondition() {
		if (!player.inventory.v.isEmpty()) {
			Iterator<Item> iterator = player.inventory.v.iterator();
			while(iterator.hasNext()) {
				Item tmp_item = iterator.next();
				if (tmp_item.isRequired) {
					displayLine("You win because you have found the " + tmp_item.dispName);
				}
			}
		}
	}
	
	public String standardName(String name) {		
		return name.replaceAll("[\\s_]", "");
	}

	public void listLocations() {
		Iterator<Location> iterator = locations.iterator();
		while(iterator.hasNext()) {
			Location tmp = iterator.next();
			displayLine(tmp.name);
		}
	}
	
	public void listItems() {
		Iterator<Item> iterator = itemList.iterator();
		while(iterator.hasNext()) {
			Item tmp = iterator.next();
			displayLine(tmp.dispName + " -- " + tmp.name);
		}
	}
	
	public void listLinks() {
		displayLine(currentLocation.showLinks());
	}
	
	public void listDoors() {
		displayLine(currentLocation.showDoors());
	}
	
	public void setGuiImg() {
		if (currentLocation.image.isEmpty()) {
			gui.setIcon("");
		} else {
			gui.setIcon(currentLocation.image);
		}
	}
	
	public void displayLine(String phrase) {
		gui.addCommand(phrase+"\n");
	}
	
	public void display(String phrase) {
		gui.addCommand(phrase);
	}
	
	public Items buildItem(Item item) {
		Items.Builder newItem = Items.newBuilder();
		
		newItem.setWeight(item.weight);
		newItem.setName(item.name);
		newItem.setCanPickup(item.canBeCarried);
		
		if (!item.pickupMessage.isEmpty()) {
			Items.DisplayMessage.Builder pickupMessage = 
				Items.DisplayMessage.newBuilder().setMessage(item.pickupMessage);
			pickupMessage.setType(Items.MessageType.PICKUP);
			newItem.addMessage(pickupMessage);
		}
		
		if (!item.dropMessage.isEmpty()) {
			Items.DisplayMessage.Builder dropMessage = 
				Items.DisplayMessage.newBuilder().setMessage(item.dropMessage);
			dropMessage.setType(Items.MessageType.DROP);
			newItem.addMessage(dropMessage);
		}

		if (!item.cannotPickupMessage.isEmpty()) {
			Items.DisplayMessage.Builder cannotPickupMessage = 
				Items.DisplayMessage.newBuilder().setMessage(item.cannotPickupMessage);
			cannotPickupMessage.setType(Items.MessageType.CANNOTPICKUP);
			newItem.addMessage(cannotPickupMessage);
		}
		return newItem.build();
	}
	
}
